package fsnotify

import (
	"errors"
	"golang.org/x/sys/unix"
)

type fdPolier struct {
	fd int //File descriptor as returned by the inotify_init() syscall
	epfd int //Epoll file descriptor
	pipe [2]int //Pipe for waking up
}

func emptyPoller(fd int) *fdPolier  {
	poller := new(fdPolier)
	poller.fd = fd
	poller.epfd = -1
	poller.pipe[0] = -1
	poller.pipe[1] = -1
	return poller
}
//Create a new inotify poller.
// This creates an inotify handler,and an epoll handler.
//var errno error
// defer func
// if errno !=nil
// close
// close
//for
// return
// iscloded
// ok,errno
// errno!=ni
// select
// errorchanel
// closed done
// return
// cotinue
// ok! contiuyne
// n,ok := read
// if iscode reun
//k ouy
// n<
// errno
// select error chanl
// dourhcae chanel
// reyurn
//coymne
//n 读取 offset
// mask file
//
func newFdPoller(fd int)(*fdPolier,error)  {
	var errno error
	poller := emptyPoller(fd)
	defer func() {
		if errno !=nil{
			poller.close()
		}
	}()
	poller.fd = fd
	//Create epoll fd  内在可以处理完成
	poller.epfd,errno = unix.EpollCreate(unix.EPOLL_CLOEXEC)
	if poller.epfd== -1 {
		return nil,errno
	}
	// Create pipe; pipe[0] is the read end, pipe[1] the write end.
	errno = unix.Pipe2(poller.pipe[:],unix.O_NONBLOCK|unix.O_CLOEXEC)
	if errno !=nil{
		return nil, errno
	}
	//Register inotify fd with epoll
	event := unix.EpollEvent{
		Fd:int32(poller.fd),
		Events:unix.EPOLLIN,
	}
	errno = unix.EpollCtl(poller.epfd,unix.EPOLL_CTL_ADD,poller.fd,&event)
	if errno !=nil{
		return nil, errno
	}
	//Register pipe fd with epoll
	event = unix.EpollEvent{
		Fd:int32(poller.pipe[0]),
		Events:unix.EPOLLIN,
	}
	errno = unix.EpollCtl(poller.epfd,unix.EPOLL_CTL_ADD,poller.pipe[0].&event)
	if errno !=nil{
		return nil, errno
	}
	return poller,nil
}
//Wait using epoll.
//Returns true if something is ready to be read,
//false if there is not.
func (poller *fdPolier) wait()(bool,error) {
	// 3 possible events per fd, and 2 fds,makes a maximum of 6 events.
	// I don't konw whether epoll_wait returns the number of events returned.
	// or the total number of events ready.
	// I decided to catch both by makinbg the buffer  one larger than the maximun.
	events := make([]unix.EpollEvent,7)
	for {
		n,errno:= unix.EpollWait(poller.epfd,events,-1)
		if n == -1 {
			if errno == unix.EINTR {
				continue
			}
			return false,errno
		}
		if n == 0 {
			// if there are no events, try again.
			continue
		}
		if n > 6 {
			//This should-never happen. More events were returned than should be possible.
			return false,errors.New("epoll_wait returned more events than I kow what to do with")
		}
		ready:= events[:n]
		epollhup := false
		epollerr := false
		epollin:= false
		for _,event := range ready {
			if event.Fd == int32(poller.fd){
				if event.Events & unix.EPOLLHUP != 0{
					// This should not hanppen but if it does, treat it as a wakeup.
					epollhup = true
				}
				if event.Events & unix.EPOLLERR != 0 {
					epollerr = true
				}
				if event.Events & unix.EPOLLIN != 0 {
					epollin = true
				}
			}

			if event.Fd == int32(poller.pipe[0]) {
				if event.Events&unix.EPOLLHUP != 0{
					//Write pipe descriptor was closed, by us this means we're cloing down the
					// watcher, and we should wake up.
				}
				if event.Events&unix.EPOLLERR != 0{
					// if an error is waitting on the pipe file descriptor.
					// this is an absolute mystery and should never happen.
					return false,errors.New("Error on the pipe descriptor.")
				}
				if event.Events&unix.EPOLLIN != 0{
					//This is a regular wakeup, so we have to clear the buffer.
					err:= poller.clearWake()
					if err != nil {
						return false,err
					}
				}

			}

		}

		if epollhup || epollerr || epollin {
			return true,nil
		}
		return false,nil
		
	}
}
//Close the write end of the poller.
func (poller *fdPolier) wake() error  {
	// 一个字节的处理方式
	// t := time.now
	// t.format
	// 20060102150405
	// byte 1 detailm
	buf := make([]byte,1)
	n,errno := unix.Write(poller.pipe[1],buf)
	if n == -1 {
		if errno == unix.EGAIN {
			// buffer is full poller will wake
			return nil
		}
		return errno
	}
	return nil
	//byte b := make([]byte,1)
	// n,errorno := unix.wrte fdpoler.iple[1]
	// if n == -1
	// errno == unbix.EGAIN
	//  buf is full wake up epoll
	// return nill
	// n!
	// returne rrno
	// retun nil
}
func (poller *fdPolier) clearWake() error  {
	//You have to be woken up a LOT in order to get to 100:
	buf := make([]byte,100)
	n,errno := unix.Read(poller.pipe[0],buf)
	if n == -1 {
		if errno == unix.EGAIN {
			//buffer is empty someone els cleared our wake.
			return nil
		}
		return errno
	}
	return nil
}
// close all poller file descriptor but not the one passed to it.
func (poller *fdPolier) close()  {
	if poller.pipe[1] != -1 {
		unix.Close(poller.pipe[1])
	}
	if poller.pipe[0] != -1 {
		unix.Close(poller.pipe[0])
	}
	if poller.epfd != -1 {
		unix.Close(poller.epfd)
	}
}
